package io.adrainty.bolee.framework.web.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.collection.CollUtil;
import io.adrainty.bolee.framework.commons.basic.ResponseCode;
import io.adrainty.bolee.framework.commons.basic.ResultDTO;
import io.adrainty.bolee.framework.commons.exception.AppException;
import io.adrainty.bolee.framework.commons.utils.ResultDTOUtil;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 * @author AdRainty
 * @version V1.0.0
 * @since 2024/5/15 0:31
 */

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(AppException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultDTO<Object> handlerAppException(AppException ex) {
        log.error("业务异常: ", ex);
        return ResultDTOUtil.error(ex.getCode(), ex.getMessage());
    }

    // @ExceptionHandler(DuplicateKeyException.class)
    // @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    // public ResultDTO<Object> handlerDuplicateKeyException(DuplicateKeyException ex) {
    //     SQLIntegrityConstraintViolationException cause = (SQLIntegrityConstraintViolationException) ex.getCause();
    //     String errorMsg = cause.getMessage();
    //     log.error("DuplicateKeyException: {}", errorMsg);
    //     return ResultDTOUtil.error(ResponseCode.DUPLICATE_KEY);
    // }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(@Nonnull MethodArgumentNotValidException ex,
                                                                  @Nonnull HttpHeaders headers,
                                                                  @Nonnull HttpStatusCode statusCode,
                                                                  @Nonnull WebRequest request) {
        BindingResult bindingResult = ex.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        ResultDTO<Object> resultDTO;
        if (statusCode instanceof HttpStatus status) {
            if (CollUtil.isEmpty(fieldErrors)) { // Something wrong
                resultDTO = ResultDTOUtil.error(statusCode.value(), status.getReasonPhrase());
            } else {
                Map<String, String> errorMsg = fieldErrors.stream().collect(Collectors.toMap(FieldError::getField, e -> {
                    if (StringUtils.isNotBlank(e.getDefaultMessage())) return e.getDefaultMessage();
                    return String.format("Field [%s] is illegal, currentValue: [%s]", e.getField(), e.getRejectedValue());
                }));
                log.error("Illegal parameter: {}", errorMsg);
                // 请求状态码修改为422, 表示请求格式正确, 但是由于含有语义错误, 无法响应
                statusCode = HttpStatus.UNPROCESSABLE_ENTITY;
                resultDTO = ResultDTOUtil.error(Integer.parseInt(ResponseCode.ILLEGAL_PARAMETER.getCode()), ResponseCode.ILLEGAL_PARAMETER.getDesc(), errorMsg);
            }
            return new ResponseEntity<>(resultDTO, headers, statusCode);
        }
        return super.handleMethodArgumentNotValid(ex, headers, statusCode, request);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultDTO<Object> handlerException(Exception ex) {
        log.error("Error happened: ", ex);
        return ResultDTOUtil.error(ResponseCode.UN_ERROR);
    }

    @ExceptionHandler(NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ResultDTO<Object> handlerNotLoginException(NotLoginException ex) {
        log.error("Error happened: ", ex);
        return ResultDTOUtil.error(Integer.parseInt(ResponseCode.USER_NOT_LOGIN.getCode()), ex.getMessage());
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(@Nonnull Exception ex, Object body,
                                                             @Nonnull HttpHeaders headers,
                                                             @Nonnull HttpStatusCode statusCode,
                                                             @Nonnull WebRequest request) {
        log.error("Request error happened: ", ex);
        if (statusCode instanceof HttpStatus status) {
            ResultDTO<Object> resultDTO = ResultDTOUtil.error(statusCode.value(), status.getReasonPhrase());
            return new ResponseEntity<>(resultDTO, headers, statusCode);
        }
        return super.handleExceptionInternal(ex, body, headers, statusCode, request);
    }
}
